import * as cc from 'cc';
import { KeyInput } from './core/KeyInput';
import { AttackAnimation } from './AttackAnination';
import { BattleUnit } from './BattleUnit';
import { PlayerInputHandler } from './core/InputHandler';
import { PlayerStateMachine } from './player_state/PlayerStateMachine';
import { AnimationController } from './animator/AnimationController';
import { HeroInputHandler } from './core/HeroInputHandler';
const { ccclass, property } = cc._decorator;

// v0.1
@ccclass('Hero')
export class Hero extends cc.Component {
    // @property(AttackAnimation)
    // attackAnimation: AttackAnimation = null!;

    @property(AnimationController)
    animationController: AnimationController = null!;

    @property
    speed: number = 15;

    @property
    dashSpeed: number = 30;

    @property
    jumpHeight: number = 25;

    @property
    onWallSpeed: number = 5;

    keyInput: KeyInput = null!;
    rigid: cc.RigidBody2D = null!;

    private _wallJumpLock: boolean = false; // 蹬墙跳锁定移动
    private _isOnWall = false;
    private _isGround: boolean = false;
    private _attack: boolean = false;
    private _canDoubleJump: boolean = false;
    private _isDashing: boolean = false;
    private _lookAtRight: boolean = true;
    private _moveX = 0;

    battle: BattleUnit = new BattleUnit(100, 10, 5);
    handle: HeroInputHandler = null!;

    stateMachine = new PlayerStateMachine();

    _startPos = cc.v3();
    flipNode: cc.Node = null;
    onLoad() {
        this.stateMachine = this.node.addComponent(PlayerStateMachine);
        const physics2D = cc.PhysicsSystem2D.instance;
        // 注册全局碰撞回调函数
        if (physics2D) {
            physics2D.enable = true;
            physics2D.on(
                cc.Contact2DType.BEGIN_CONTACT,
                this.onBeginContact,
                this,
            );
            physics2D.on(
                cc.Contact2DType.END_CONTACT,
                this.onEndContact,
                this,
            );
        }

        this.keyInput = this.node.addComponent(KeyInput);
        this.rigid = this.node.getComponent(cc.RigidBody2D);
        this.handle = this.addComponent(HeroInputHandler);
        this.battle.event.on(
            'die',
            () => {
                console.log('Player Die');
            },
            this,
        );

        this._startPos = this.animationController.node.position.clone();
        this.flipNode = this.node.getChildByName('flip');
    }

    update(deltaTime: number) {
        this.handleGround();
        this.handleOnWall();
        this.dashAction();

        this.updateContext();
        this.updateAnimator();
    }

    updateContext() {
        this.stateMachine.gameContext.moveX = this._moveX;
        this.stateMachine.gameContext.moveY = this.rigid.linearVelocity.y;
        this.stateMachine.gameContext.isGround = this._isGround;
        this.stateMachine.gameContext.isDash = this._isDashing;
        this.stateMachine.gameContext.isOnWall = this._isOnWall;
        this.stateMachine.gameContext.isAttack = this._attack;
    }

    updateAnimator() {
        this.animationController.play(this.stateMachine.getState());
        if (this.stateMachine.getState() == 'climb') {
            this.animationController.node.position = cc.v3(10.8, 4.3, 0);
        } else {
            this.animationController.node.position = this._startPos;
        }
    }

    moveTo(x: number) {
        if (this._isDashing || this._wallJumpLock) {
            return;
        }
        if (this._attack && this._isGround) {
            x = 0;
        } else if (x > 0) {
            this.changeLookAt(true);
        } else if (x < 0) {
            this.changeLookAt(false);
        }
        const velocity = this.rigid.linearVelocity.clone();
        velocity.x = x * this.speed;
        this.rigid.linearVelocity = velocity;
        this._moveX = x;
    }

    jump() {
        if (this._isOnWall) {
            this.jumpByWall();
            return;
        }
        if (!this._isGround) {
            this.doubleJump();
            return;
        }
        const velocity = this.rigid.linearVelocity.clone();
        velocity.y = this.jumpHeight;
        this.rigid.linearVelocity = velocity;
        this._isGround = false;
    }

    doubleJump() {
        if (!this._canDoubleJump) {
            return;
        }
        this._canDoubleJump = false;
        const velocity = this.rigid.linearVelocity.clone();
        velocity.y = this.jumpHeight;
        this.rigid.linearVelocity = velocity;
    }

    jumpByWall() {
        if (!this._isOnWall) {
            return;
        }
        const velocity = this.rigid.linearVelocity.clone();
        velocity.y = this.jumpHeight;
        velocity.x = this.speed * (this._lookAtRight ? -1 : 1);
        this.rigid.linearVelocity = velocity;
        this._isOnWall = false;
        this._wallJumpLock = true;
        // 0.15秒后恢复移动输入
        this.scheduleOnce(() => {
            this._wallJumpLock = false;
        }, 0.15);
    }

    dashAction() {
        if (!this._isDashing) {
            return;
        }
        const velocity = this.rigid.linearVelocity.clone();
        velocity.x = this.dashSpeed * (this._lookAtRight ? 1 : -1);
        velocity.y = 0;
        this.rigid.linearVelocity = velocity;
    }

    dash() {
        if (this._isDashing) {
            return;
        }
        this._isDashing = true;
        this.scheduleOnce(() => {
            this._isDashing = false;
        }, 0.25);
    }

    handleGround() {
        if (this.checkOnGround()) {
            this._isGround = true;
            this._canDoubleJump = true;
        } else {
            this._isGround = false;
        }
    }

    // 空中贴墙
    handleOnWall() {
        const velocity = this.rigid.linearVelocity.clone();
        this._isOnWall =
            this.checkOnWall() && !this._isGround && velocity.y < 0;
        if (this._isOnWall) {
            velocity.y = Math.max(-this.onWallSpeed, velocity.y);
            this.rigid.linearVelocity = velocity;
        }
    }

    attack() {
        if (this.keyInput.getKeyDown(cc.KeyCode.KEY_J)) {
        }
        if (this._attack) {
            return;
        }
        this._attack = true;
        this.attackAction();
        this.scheduleOnce(() => {
            this._attack = false;
        }, 0.5);
    }

    attackAction() {
        // this.attackAnimation.attack();
    }

    checkOnGround() {
        // 已经在上升或下落的过程中了
        if (Math.abs(this.rigid.linearVelocity.y) > 2) {
            return false;
        }

        const wPos = this.node.worldPosition.clone();

        const leftStart = cc.v3(wPos.x - 25, wPos.y, wPos.z);
        const leftEnd = cc.v3(wPos.x - 25, wPos.y - 51, wPos.z);
        const rightStart = cc.v3(wPos.x + 25, wPos.y, wPos.z);
        const rightEnd = cc.v3(wPos.x + 25, wPos.y - 51, wPos.z);
        const result_1 = cc.PhysicsSystem2D.instance.raycast(
            leftStart,
            leftEnd,
        );

        const result_2 = cc.PhysicsSystem2D.instance.raycast(
            rightStart,
            rightEnd,
        );
        if (result_1.length + result_2.length > 0) {
            return true;
        }
        return false;
    }

    checkOnWall() {
        const wPos = this.node.worldPosition.clone();
        const end = cc.v3(wPos);
        end.x += 26 * (this._lookAtRight ? 1 : -1);
        const result = cc.PhysicsSystem2D.instance.raycast(wPos, end);
        if (result.length > 0) {
            return true;
        }
        return false;
    }

    changeLookAt(right: boolean) {
        this._lookAtRight = right;
        if (right) {
            this.flipNode.scale = cc.v3(1, 1, 1);
        } else {
            this.flipNode.scale = cc.v3(-1, 1, 1);
        }
    }

    onBeginContact(
        selfCollider: cc.Collider2D,
        otherCollider: cc.Collider2D,
        contact: cc.IPhysics2DContact | null,
    ) {
        if (otherCollider.tag === 1) {
            // 敌人
            this.battle.takeDamage(20);
            // this.debugLabel.string = `HP:${this.battle.health}`;
            console.log('Player HP:', this.battle.health);
        }
    }

    onEndContact(
        contact: cc.IPhysics2DContact | null,
        selfCollider: cc.Collider2D,
        otherCollider: cc.Collider2D,
    ) {}
}
